﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using StripeOne.Core.S1Tools;
using StripeOne.Core.S1Cache;
using StripeOne.Core.S1Config;

namespace StripeOne.Core.XmlConfig
{
	public class XmlConfig : XmlObject
	{
		public XmlConfig() : this("default") { }
		public XmlConfig(string section) : base(section) { }

		public XmlObject Default { get; set; }
		public string ConfigFile { get; set; }

		private int _cacheTime = 0;

		public int CacheTime
		{
			get {
				return _cacheTime; 
			}
			set { _cacheTime = value; }
		}

		public XmlConfig(string configName, string section, string listInstanceName, string instanceName)
			: this(configName, section)
		{
			this.Default = GetDefaultInstance(configName, listInstanceName, instanceName);
		}
		
		public XmlConfig(string configName, string section)
		{
			this.XmlElement = XmlObjectTool.GetXmlElement(GeneralTools.GetConfigFolderPath() + configName, section, true, CacheTime);
			this.ConfigFile = configName;
		}

		#region DefaultInstance

		private XmlObject GetDefaultInstance(string configName, string listInstanceName, string instanceName)
		{
			string cacheKey = ConfigFile + "Xmlconfig_defaultinstance_cachekey_" + configName;

			object obj = S1Cache.S1Cache.GetObject(cacheKey);
			if(obj != null && obj is XmlObject)
			{
				return (XmlObject)obj;
			}
			XmlObject instance = GetInstances(configName, listInstanceName, instanceName).Find(i => i.GetXmlAttributeValue("default").ToBoolean() || (i.XmlElement != null && i.XmlElement.Name.ToLower() == "default"));
			if(instance == null)
			{
				instance = new XmlObject();
			}
			S1Cache.S1Cache.Add(instance, cacheKey, CacheTime);
			return instance;
		}

		private List<XmlObject> GetInstances(string configName, string listInstanceName, string instanceName)
		{
            return S1Cache.S1Cache.AutoCache<List<XmlObject>>(ConfigFile + "_Xmlconfig_instances", CacheTime, () => GetInstancesNoCache(configName, listInstanceName, instanceName));
		}

		private List<XmlObject> GetInstancesNoCache(string configName, string listInstanceName, string instanceName)
		{
			try
			{
				return XmlObjectTool.GetXmlObjects<XmlObject>(GeneralTools.GetConfigFolderPath() + configName, listInstanceName, instanceName, true,  CacheTime);
			}
			catch
			{
				return new List<XmlObject>();
			}
		}

		#endregion

		public bool IsDefault
		{
			get { return this.GetXmlAttributeValue("default").ToBoolean() || (this.XmlElement != null && this.XmlElement.Name.ToLower() == "default"); }
		}

		public List<T> GetXmlConfigs<T>(string session, string path) where T : XmlConfig
		{
			List<T> toReturn = this.GetXmlObjects<T>(session, path);
			return this.MergeLis<T>(toReturn, session, path);
		}

		public T GetXmlConfig<T>(string session) where T : XmlConfig
		{
			T xmlObj = this.GetXmlObject<T>(session);
			return this.SetDefault<T>(xmlObj, session);
		}

		#region SetDefault - MergeLis

		public T SetDefault<T>(T mergeIn, string section) where T : XmlConfig
		{
			if(this.Default != null && this.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
			{
				T mergeFrom = this.Default.GetXmlObject<T>(section);
				if(mergeFrom.GetXmlAttributeValue(mergeFrom.Section, "use_default", "true").ToBoolean()) mergeIn.Default = mergeFrom;
			}
			return mergeIn;
		}

		public List<T> MergeLis<T>(List<T> mergeIn, string section, string path) where T : XmlConfig
		{
			if(this.Default != null && this.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
			{
				List<T> mergeFrom = this.Default.GetXmlObjects<T>(section, path);

				if(!this.Default.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
				{
					mergeFrom = mergeFrom.FindAll(o => o.GetXmlAttributeValue(o.Section, "use_default", "true").ToBoolean());
				}
				foreach(T objXml in mergeFrom)
				{
					if(mergeIn.Find(o => o.Id == objXml.Id) == null)
					{
						mergeIn.Add(objXml);
					}
					else
					{
						mergeIn.Find(o => o.Id == objXml.Id).Default = objXml;
					}
				}
			}
			return mergeIn;
		}

		#endregion

		#region ParseFields

		public override string GetXmlAttributeValue(string attributeName)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetXmlAttributeValue(attributeName);
			return S1Parser.ToString(base.GetXmlAttributeValue(attributeName), this.Default.GetXmlAttributeValue(attributeName));
		}
		public override string GetXmlAttributeValue(string section, string attributeName)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetXmlAttributeValue(section, attributeName);
			return S1Parser.ToString(base.GetXmlAttributeValue(section, attributeName), this.Default.GetXmlAttributeValue(section, attributeName));
		}
		public override string GetXmlAttributeValue(string section, string attributeName, string defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetXmlAttributeValue(section, attributeName, defaultValue);
			return S1Parser.ToString(base.GetXmlAttributeValue(section, attributeName), this.Default.GetXmlAttributeValue(section, attributeName, defaultValue));
		}
		public override string GetString(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetString(fieldname);
			return base.GetString(fieldname).ToString(this.Default.GetString(fieldname));
		}
		public override string GetString(string fieldname, string defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetString(fieldname, defaultValue);
			return S1Parser.ToString(base.GetString(fieldname), this.Default.GetString(fieldname, defaultValue));
		}
		public override int GetInt32(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetInt32(fieldname);
			return base.GetString(fieldname).ToInt32(this.Default.GetInt32(fieldname));
		}
		public override int GetInt32(string fieldname, int defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetInt32(fieldname, defaultValue);
			return base.GetString(fieldname).ToInt32(this.Default.GetInt32(fieldname, defaultValue));
		}
		public override bool GetBoolean(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetBoolean(fieldname);
			return base.GetString(fieldname).ToBoolean(this.Default.GetBoolean(fieldname));
		}
		public override bool GetBoolean(string fieldname, bool defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetBoolean(fieldname, defaultValue);
			return base.GetString(fieldname).ToBoolean(this.Default.GetBoolean(fieldname, defaultValue));
		}
		public override float GetFloat(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetFloat(fieldname);
			return base.GetString(fieldname).ToFloat(this.Default.GetFloat(fieldname));
		}
		public override float GetFloat(string fieldname, float defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetFloat(fieldname, defaultValue);
			return base.GetString(fieldname).ToFloat(this.Default.GetFloat(fieldname, defaultValue));
		}
		public override double GetDouble(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDouble(fieldname);
			return base.GetString(fieldname).ToDouble(this.Default.GetDouble(fieldname));
		}
		public override double GetDouble(string fieldname, double defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDouble(fieldname, defaultValue);
			return base.GetString(fieldname).ToDouble(this.Default.GetDouble(fieldname, defaultValue));
		}
		public override decimal GetDecimal(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDecimal(fieldname);
			return base.GetString(fieldname).ToDecimal(this.Default.GetDecimal(fieldname));
		}
		public override decimal GetDecimal(string fieldname, decimal defaultValue)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDecimal(fieldname, defaultValue);
			return base.GetString(fieldname).ToDecimal(this.Default.GetDecimal(fieldname, defaultValue));
		}
		public override decimal GetDecimal(string fieldname, string cultureName)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDecimal(fieldname, cultureName);
			return base.GetString(fieldname).ToDecimal(this.Default.GetDecimal(fieldname, cultureName));
		}
		public override DateTime GetDateTime(string fieldname)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDateTime(fieldname);
			return base.GetString(fieldname).ToDateTime(this.Default.GetDateTime(fieldname));
		}
		public override DateTime GetDateTime(string fieldname, string cultureName)
		{
			if(this.Default == null || this.Equals(this.Default)) return base.GetDateTime(fieldname, cultureName);
			return base.GetString(fieldname).ToDateTime(this.Default.GetDateTime(fieldname, cultureName));
		}

		#endregion
	}

    public class XmlS1Config : S1Config.S1Config
    {
        public XmlS1Config(string configName) : this(configName, "default") { }

        public XmlS1Config(string configName, string section) : this(configName, section, true, true) { }

        public XmlS1Config(string configName, string section, bool useCache, bool inheritFromDefault)
            : base(configName, section, useCache, inheritFromDefault)
        {
            this.Wrapper = XmlObjectTool.GetXmlObject<XmlObject>(GeneralTools.GetConfigFolderPath() + configName, section, true, KeyCache, CacheTime);
            this.Default = XmlObjectTool.GetXmlObject<XmlObject>(GeneralTools.GetConfigFolderPath() + configName, "default", true, KeyCache, CacheTime);
        }

        private XmlObject Default { get; set; }
        private XmlObject Wrapper { get; set; }

        private string _keyCache;

        public string KeyCache
        {
            get
            {
                if (String.IsNullOrEmpty(_keyCache)) _keyCache = String.Empty;
                return _keyCache;
            }
            set { _keyCache = value; }
        }

        private int _cacheTime = 0;

        public int CacheTime
        {
            get
            {
                return _cacheTime;
            }
            set { _cacheTime = value; }
        }

        public List<T> GetXmlConfigs<T>(string session, string path) where T : XmlConfig
        {
            List<T> toReturn = this.Wrapper.GetXmlObjects<T>(session, path);
            return this.MergeLis<T>(toReturn, session, path);
        }

        public T GetXmlConfig<T>(string session) where T : XmlConfig
        {
            T xmlObj = this.Wrapper.GetXmlObject<T>(session);
            return this.SetDefault<T>(xmlObj, session);
        }

        #region ParseFields

        public string GetXmlAttributeValue(string attributeName)
        {
            if (this.Default == null || this.Default.Equals(this.Wrapper)) return this.Wrapper.GetXmlAttributeValue(attributeName);
            return S1Parser.ToString(this.Wrapper.GetXmlAttributeValue(attributeName), this.Default.GetXmlAttributeValue(attributeName));
        }

        public string GetXmlAttributeValue(string section, string attributeName)
        {
            if (this.Default == null || this.Default.Equals(this.Wrapper)) return this.Wrapper.GetXmlAttributeValue(section, attributeName);
            return S1Parser.ToString(this.Wrapper.GetXmlAttributeValue(section, attributeName), this.Default.GetXmlAttributeValue(section, attributeName));
        }

        public string GetXmlAttributeValue(string section, string attributeName, string defaultValue)
        {
            if (this.Default == null || this.Default.Equals(this.Wrapper)) return this.Wrapper.GetXmlAttributeValue(section, attributeName, defaultValue);
            return S1Parser.ToString(this.Wrapper.GetXmlAttributeValue(section, attributeName), this.Default.GetXmlAttributeValue(section, attributeName, defaultValue));
        }

        #endregion

        #region ValidateConfig

        public static bool ValidateConfig(string configName, string configSection)
        {
            string validate = null;
            string[] sections = S1Config.S1Config.GetSections(configName);
            if (sections != null) validate = sections.ToList().Find(c => c.ToLower().Equals(configSection.ToLower()));
            return !String.IsNullOrEmpty(validate);
        }

        #endregion

        #region SetDefault - MergeLis

        public T SetDefault<T>(T mergeIn, string section) where T : XmlConfig
        {
            if (this.Default != null && this.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
            {
                T mergeFrom = this.Default.GetXmlObject<T>(section);
                if (mergeFrom.GetXmlAttributeValue(mergeFrom.Section, "use_default", "true").ToBoolean()) mergeIn.Default = mergeFrom;
            }
            return mergeIn;
        }

        public List<T> MergeLis<T>(List<T> mergeIn, string section, string path) where T : XmlConfig
        {
            if (this.Default != null && this.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
            {
                List<T> mergeFrom = this.Default.GetXmlObjects<T>(section, path);

                if (!this.Default.GetXmlAttributeValue(section, "use_default", "true").ToBoolean())
                {
                    mergeFrom = mergeFrom.FindAll(o => o.GetXmlAttributeValue(o.Section, "use_default", "true").ToBoolean());
                }
                // filtering only with "id"
                mergeFrom = mergeFrom.FindAll(o => o.Id > 0);

                foreach (T objXml in mergeFrom)
                {
                    if (mergeIn.Find(o => o.Id == objXml.Id) == null)
                    {
                        mergeIn.Add(objXml);
                    }
                    else
                    {
                        mergeIn.Find(o => o.Id == objXml.Id).Default = objXml;
                    }
                }
            }
            return mergeIn;
        }

        #endregion
    }
}
